/*
 * util.c - cve-check-tool
 *
 * Copyright (C) 2015 Intel Corporation
 *
 * cve-check-tool is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#define _GNU_SOURCE
#include <dirent.h>
#include <gio/gio.h>
#include <glib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "cve-check-tool.h"
#include "cve-string.h"
#include "util.h"

bool find_sources(const char *path, package_match_func match, bool recurse, cve_add_callback cb)
{
        struct stat st = {.st_ino = 0 };
        bool ret = false;
        DIR *dir = NULL;
        struct dirent *ent = NULL;
        char *fullp = NULL;

        if (!cb) {
                return false;
        }

        if (!match) {
                return false;
        }

        if (lstat(path, &st) != 0) {
                goto end;
        }

        if (S_ISLNK(st.st_mode)) {
                ret = false;
                goto end;
        } else if (S_ISDIR(st.st_mode)) {
                if (!(dir = opendir(path))) {
                        goto end;
                }
                while ((ent = readdir(dir))) {
                        if (!streq(ent->d_name, ".") && !streq(ent->d_name, "..")) {
                                if (!asprintf(&fullp, "%s/%s", path, ent->d_name)) {
                                        goto end;
                                }
                                if (!(cve_is_dir(fullp) && !recurse)) {
                                        find_sources(fullp, match, recurse, cb);
                                }
                                free(fullp);
                        }
                }
        } else if (S_ISREG(st.st_mode)) {
                if (match(path)) {
                        cb(path);
                }
        }

        ret = true;
end:
        if (dir) {
                closedir(dir);
        }
        return ret;
}

bool is_package_list(cve_string *path)
{
        if (cve_string_has_suffix(path, "packages") || cve_string_has_suffix(path, "packages-nvr")) {
                return cve_file_exists(path->str);
        }
        return false;
}

gchar *demacro(CveHashmap *macros, gchar *str)
{
        gchar *key = NULL, *value = NULL;

        if (!macros) {
                return str;
        }

        while (true) {
                bool hit = false;
                CveHashmapIter iter;

                cve_hashmap_iter_init(macros, &iter);
                while (cve_hashmap_iter_next(&iter, (void **)&key, (void **)&value)) {
                        if (str_contains(str, key)) {
                                hit = true;
                                str = str_replace(str, key, value);
                        }
                }
                if (!hit) {
                        break;
                }
        }
        return str;
}

int64_t parse_xml_date(const char *date)
{
        autofree(cve_string) *tmp = cve_string_dup(date);
        autofree(GTimeZone) *tz = NULL;
        autofree(GDateTime) *t = NULL, *t2 = NULL;
        char *c = NULL;
        int64_t ret = -1;

        /* Example XML string:
         * 2015-03-05T08:24:10.220-05:00
         */
        if (!tmp) {
                return -1;
        }
        if (!(c = memchr(tmp->str, 'T', tmp->len))) {
                return -1;
        }
        if (!(c = memchr(c, '-', tmp->len - (tmp->str - c)))) {
                return -1;
        }
        gint y, m, d, h, min, s;
        if (sscanf(date, "%4d-%2d-%2dT%2d:%2d:%2d", &y, &m, &d, &h, &min, &s) != 6) {
                return -1;
        }
        tz = g_time_zone_new(c);
        if (!tz) {
                return -1;
        }

        t = g_date_time_new(tz, y, m, d, h, min, (gdouble)s);
        if (!t) {
                return -1;
        }
        t2 = g_date_time_to_local(t);

        ret = (int64_t)g_date_time_to_unix(t2);

        return ret;
}

gchar *str_replace(gchar *source, const gchar *word, const gchar *replace)
{
        autofree(gstrv) *splits = NULL;
        gchar *ret = NULL;
        splits = g_strsplit(source, word, -1);
        ret = g_strjoinv(replace, splits);
        g_free(source);
        return ret;
}

/* File functions */

bool cve_file_exists(const char *p)
{
        struct stat st = {.st_ino = 0 };
        return (stat(p, &st) == 0);
}

bool cve_is_dir(const char *p)
{
        struct stat st = {.st_ino = 0 };
        if (stat(p, &st) != 0) {
                return false;
        }
        return S_ISDIR(st.st_mode);
}

char *cve_get_file_parent(const char *p)
{
        autofree(char) *d = strdup(p);
        char *r = realpath(dirname(d), NULL);
        return r;
}

bool cve_file_set_text(const char *path, char *text)
{
        FILE *fp = NULL;
        bool ret = false;

        fp = fopen(path, "w");

        if (!fp) {
                goto end;
        }

        if (fprintf(fp, "%s", text) < 0) {
                goto end;
        }
        ret = true;
end:
        if (fp) {
                fclose(fp);
        }

        return ret;
}

cve_string *make_db_dot_fname(const char *db_path, const char *suffix)
{
        autofree(char) *path = NULL;
        const char *dir;
        char *file;

        path = strdup(db_path);
        if (!path) {
                return NULL;
        }

        file = strrchr(path, '/');
        if (file) {
                *file++ = '\0';
                if (!*file) {
                        file = (char *)nvd_file;
                }
                dir = *path ? path : ".";
        } else {
                file = path;
                dir = ".";
        }

        return cve_string_dup_printf("%s/.%s.%s", dir, file, suffix);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 expandtab:
 * :indentSize=8:tabSize=8:noTabs=true:
 */
